
/*MODIFY_DOG{*/
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <syslog.h>
#include "nfqueue_handler.h"
#include "client_list.h"
#include "conf.h"
#include "debug.h"
/*}MODIFY_DOG*/

extern pthread_mutex_t client_list_mutex;

/*MODIFY_DOG{*/
static u_int32_t inspect_pkt(struct nfq_data *tb, int *payload_len) {
	int id = 0;
	struct nfqnl_msg_packet_hdr *ph;
	struct nfqnl_msg_packet_hw *hwph;
	u_int32_t mark, ifi;
	char *data;

	ph = nfq_get_msg_packet_hdr(tb);
	if (ph) {
		id = ntohl(ph->packet_id);
		debug(LOG_INFO,"hw_protocol=0x%04x hook=%u id=%u ",
				ntohs(ph->hw_protocol), ph->hook, id);
	}

	(*payload_len) = nfq_get_payload(tb, &data);

	if ((*payload_len) >= 0) {
		debug(LOG_INFO,"payload_len=%d ", (*payload_len));
	}

	return id;
}

static int cb_out(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
		struct nfq_data *nfa, void *data) {
	debug(LOG_INFO,"entering callback for outgoing packet");
	int payload_len;
	u_int32_t id = inspect_pkt(nfa, &payload_len);
	char mac[20];
	struct nfqnl_msg_packet_hw *hwph;
	hwph = nfq_get_packet_hw(nfa);
	if (hwph) {
		sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", hwph->hw_addr[0],
				hwph->hw_addr[1], hwph->hw_addr[2], hwph->hw_addr[3],
				hwph->hw_addr[4], hwph->hw_addr[5]);
		debug(LOG_INFO,"Incoming packet MAC: %s", mac);
	}
	debug(LOG_INFO, "searching for it in the clients list");
	t_client *client;
	int value = -1;
	LOCK_CLIENT_LIST();
	client = client_list_find_by_mac(mac);
	if (client != NULL) {
		debug(LOG_INFO, "Client found ip: %s and state: %i, old outgoing traffic: %d", client->ip,
				client->fw_connection_state, client->counters.outgoing);
		client->counters.outgoing += payload_len;
		debug(LOG_INFO, "current outgoing traffic: %d", client->counters.outgoing);
		value = nfq_set_verdict2(qh, id, NF_ACCEPT,
				client->fw_connection_state, 0, NULL);
		debug(LOG_INFO,"Packet marked by set_verdict2 with mark: %i",
				client->fw_connection_state);
	} else {
		debug(LOG_INFO, "Client not found!");
		value = nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
	}

	UNLOCK_CLIENT_LIST();
	debug(LOG_INFO,"exiting callback for outgoing packet");
	return value;
}

static int cb_in(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
		struct nfq_data *nfa, void *data) {
	debug(LOG_INFO,"entering callback for incoming packet");
	int payload_len;
	u_int32_t id = inspect_pkt(nfa, &payload_len);
	char mac[20];
	struct nfqnl_msg_packet_hw *hwph;
	hwph = nfq_get_packet_hw(nfa);
	if (hwph) {
		sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", hwph->hw_addr[0],
				hwph->hw_addr[1], hwph->hw_addr[2], hwph->hw_addr[3],
				hwph->hw_addr[4], hwph->hw_addr[5]);
		debug(LOG_INFO,"Incoming packet MAC: %s", mac);

	}
	debug(LOG_INFO, "searching for it in the clients list");
	t_client *client;
	int value = -1;
	LOCK_CLIENT_LIST();
	client = client_list_find_by_mac(mac);
	if (client != NULL) {
		debug(LOG_INFO, "Client found by ip: %s and state: %i, current incoming traffic: %d", client->ip,
				client->fw_connection_state, client->counters.incoming);
		printf("PAYLOAD %d\n", payload_len);
		client->counters.incoming += payload_len;
		debug(LOG_INFO, "current incoming traffic: %d", client->counters.incoming);
	} else {
		debug(LOG_INFO, "Client not found!");
	}
	UNLOCK_CLIENT_LIST();
	value = nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
	debug(LOG_INFO,"exiting callback for incoming packet");
	return value;
}

int create_nfqueue(int queue_num, nfq_callback* cb_pointer,
		struct nfq_handle** h_pointer, struct nfq_q_handle** qh_pointer) {
	debug(LOG_INFO,"Opening nfq library handle");
	(*h_pointer) = nfq_open();
	if (!(*h_pointer)) {
		debug(LOG_ERR, "FATAL: error during nfq_open() - exiting");
		termination_handler(0);
	}

	//#ifdef INSANE
	/* normally, applications SHOULD NOT issue this command, since
	 * it detaches other programs/sockets from AF_INET, too ! */
	debug(LOG_INFO, "unbinding existing nf_queue handler for AF_INET (if any)");
	int returnVal = nfq_unbind_pf((*h_pointer), AF_INET);
	if (returnVal < 0) {
		debug(LOG_ERR, "error [%d] during nfq_unbind_pf()", returnVal);
		termination_handler(0);
	}
	//#endif

	debug(LOG_INFO, "binding nfnetlink_queue as nf_queue handler for AF_INET");
	if (nfq_bind_pf((*h_pointer), AF_INET) < 0) {
		debug(LOG_ERR, "error during nfq_bind_pf()");
		termination_handler(0);
	}

	debug(LOG_INFO, "binding this socket to queue '%d'", queue_num);
	(*qh_pointer) = nfq_create_queue((*h_pointer), queue_num, cb_pointer, NULL);
	if (!(*qh_pointer)) {
		debug(LOG_ERR, "error during nfq_create_queue()");
		termination_handler(0);
	}

	debug(LOG_INFO, "setting copy_packet mode");
	if (nfq_set_mode((*qh_pointer), NFQNL_COPY_PACKET, 0xffff) < 0) {
		debug(LOG_ERR, "can't set packet_copy mode");
		termination_handler(0);
	}

	debug(LOG_INFO, "returning handler");
	return nfq_fd((*h_pointer));

}

void nfqueue_out_traffic_handler(void * arg) {
	struct nfq_handle *h;
	struct nfq_q_handle *qh;
	int fd;
	int rv;
	char buf[4096] __attribute__ ((aligned));
	s_config *config = config_get_config();
	int queue_num = config->nfqueue_out_num;
	debug(LOG_INFO, "Creating nfqueue for output traffic on queue number %d", queue_num);
	fd = create_nfqueue(queue_num, &cb_out, &h, &qh);
	debug(LOG_INFO, "starting receiving traffic in nfqueue (%d)", queue_num);
	while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
		debug(LOG_INFO, "packet received");
		nfq_handle_packet(h, buf, rv);
		debug(LOG_INFO, "packet handled");
	}
	debug(LOG_INFO, "unbinding from queue %d", queue_num);
	nfq_destroy_queue(qh);

#ifdef INSANE
	/* normally, applications SHOULD NOT issue this command, since
	 * it detaches other programs/sockets from AF_INET, too ! */
	debug(LOG_INFO, "unbinding from AF_INET");
	nfq_unbind_pf(h, AF_INET);
#endif

	debug(LOG_INFO, "closing library handle");
	nfq_close(h);
	exit(0);
}

void nfqueue_in_traffic_handler(void * arg) {
	struct nfq_handle *h;
	struct nfq_q_handle *qh;
	int fd;
	int rv;
	char buf[4096] __attribute__ ((aligned));
	s_config *config = config_get_config();
	int queue_num = config->nfqueue_in_num;
	debug(LOG_INFO, "Creating nfqueue for output traffic on queue number %d", queue_num);
	fd = create_nfqueue(queue_num, &cb_in, &h, &qh);
	debug(LOG_INFO, "starting receiving traffic in nfqueue (%d)", queue_num);
	while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
		debug(LOG_INFO, "packet received");
		nfq_handle_packet(h, buf, rv);
		debug(LOG_INFO, "packet handled");
	}
	debug(LOG_INFO, "unbinding from queue %d", queue_num);
	nfq_destroy_queue(qh);

#ifdef INSANE
	debug(LOG_INFO, "unbinding from AF_INET");
	nfq_unbind_pf(h, AF_INET);
#endif

	debug(LOG_INFO, "closing library handle");
	nfq_close(h);
	exit(0);
}
/*}MODIFY_DOG*/

